package com.yzd.learnjava.concurrency;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import com.yzd.learnjava.util.Print;

public class HorseRace {
	static final int FINISH_LINE = 75;
	private List<Horse> horses = new ArrayList<Horse>();
	private ExecutorService exec = Executors.newCachedThreadPool();
	private CyclicBarrier barrier;
	public HorseRace(int nHorses, final int pause) {
		barrier = new CyclicBarrier(nHorses, new Runnable() {
			public void run() {
				StringBuilder line = new StringBuilder();
				for(int i = 0 ; i < FINISH_LINE; i++) {
					line.append("=");
				}
				Print.print(line);
				for(Horse h : horses) {
					Print.print(h.tracks());
				}
				for(Horse horse : horses) {
					if(horse.getStrides() >= FINISH_LINE) {
						Print.print(horse + "won!");
						exec.shutdown();
						return;
					}
				}
				try {
					TimeUnit.MILLISECONDS.sleep(pause);
				} catch (InterruptedException e) {
					Print.print("Barrier-action sleep interrupted");
					e.printStackTrace();
				}
			}
		});
		for(int i = 0 ; i < nHorses; i++) {
			Horse h = new Horse(barrier);
			horses.add(h);
			exec.execute(h);
		}
	}
	
	public static void main(String[] args) {
		int nHorses = 7;
		int pause = 200;
		new HorseRace(nHorses, pause);
	}
}
class Horse implements Runnable{
	private static int counter = 0;
	private final int id = counter++;
	private int strides = 0;
	private static Random random = new Random(47);
	private static CyclicBarrier barrier;
	public Horse(CyclicBarrier barrier) {
		this.barrier = barrier;
	}
	public synchronized int getStrides() {
		return strides;
	}
	public void run() {
		try {
			while(!Thread.interrupted()) {
				synchronized (this) {
					//strides += random.nextInt(3);
					strides++;
				}
				barrier.await();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch(BrokenBarrierException e) {
			e.printStackTrace();
		}
	}
	
	public String toString() {
		return "Horse " + id + " ";
	}
	public String tracks() {
		StringBuilder sb = new StringBuilder();
		for(int i = 0 ; i < getStrides(); i++) {
			sb.append("*");
		}
		sb.append(id);
		return sb.toString();
	}
}